﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Diagnostics.Contracts;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Concurrency;
using System.Threading;

namespace ReactiveCode
{
    using Observables;
    using Expressions;
    using Util;

    interface IReactiveMethod
    {
        IObservable<object> ReturnValue { get; }
    }
    [System.Diagnostics.DebuggerTypeProxy(typeof(ReactiveMethod<>.DebugView))]
    public class ReactiveMethod<T> : ReactiveExpression, IReactiveMethod
    {
        #region DebugView

        class DebugView
        {
            ReactiveMethod<T> _method;

            public DebugView(ReactiveMethod<T> method)
            {
                Contract.Requires(method != null);
                _method = method;
            }

            public ReactiveExpression Body
            {
                get { return _method.Body; }
            }

            public bool Enabled
            {
                get { return _method.Enabled; }
            }

            public string Name
            {
                get { return _method.Name; }
            }

            public object ReturnValue
            {
                get { return _method.ReturnValue.LastOrDefault(); }
            }
        }

        #endregion

        internal ReactiveExpression Body { get; private set; }
        
        internal ReactiveMethod(ReactiveExpression body)
        {
            Contract.Requires(body != null);
            Body = body;
            Body.Parent = this;
            Body.BindEnabled(this);
        }
        public override void Dispose()
        {
            Body.Dispose();
        }

        #region Return value

        internal Variable<ReturnExpression<T>> CurrentReturnExpressionVariable { get; private set; }
        internal ReturnExpression<T> CurrentReturnExpression
        {
            get { return CurrentReturnExpressionVariable.Value; }
            set { CurrentReturnExpressionVariable.Value = value; }
        }
        public IObservable<T> ReturnValue
        {
            get
            {
                return CurrentReturnExpressionVariable.SelectMany(r => r != null ? r.Value : Observable.Return(default(T)));
            }
        }
        IObservable<object> IReactiveMethod.ReturnValue
        {
            get { return ReturnValue.Select(x => (object) x); }
        }

        #endregion
    }
    public class ReactiveMethodDefinition<T>
    {
        Func<ReactiveExpression> _body;
        public ReactiveMethodDefinition(Func<ReactiveExpression> body)
        {
            Contract.Requires<ArgumentNullException>(body != null, "body");
            _body = body;
        }

        public ReactiveMethod<T> Call()
        {
            return new ReactiveMethod<T>(_body());
        }
    }
}
